/**************************************************************/
//enemy is our standard enemy class. there's multiple different kinds that can
//do damage and follow around the player
// Tyler Wozniak & Andrew Hall
/**************************************************************/


#include "Enemy.h"
#include "GameManager.h"
#include <MMSystem.h>
#include <MMSystem.h>

//default constructor
Enemy::Enemy(void): Unit() 
{
	xPos = 10;
	yPos = 10;
	seen = false;
	defaultInit();
}

//copy constructor
Enemy::Enemy( const Enemy& other )
{
	seen = other.seen;
	name = other.name;
	strength = other.strength;
	agility = other.agility;
	stamina = other.stamina;
	xPos = other.xPos;
	yPos = other.yPos;
}

//parameterized constructor
Enemy::Enemy( string enemyType, int x, int y )
{
	xPos = x;
	yPos = y;
	seen = false;
	for(int i = 0; i < enemyType.length(); i++)
		enemyType[i] = tolower(enemyType[i]);
	tinyxml2::XMLDocument doc;
	doc.LoadFile("../Inits/Units.xml");
	if(!doc.ErrorID())
	{
		try
		{
			tinyxml2::XMLElement* enemyElement = doc.FirstChildElement( "enemies" )->FirstChildElement(enemyType.c_str());
			if(!enemyElement)
				throw 0;
			name = enemyElement->Attribute("name");
			strength = enemyElement->IntAttribute("strength");
			agility = enemyElement->IntAttribute("agility");
			stamina = enemyElement->IntAttribute("stamina");
			if(name.empty() || !strength || !agility || !stamina)
			{
				defaultInit();
			}
		}
		catch (int e)
		{
			defaultInit();
		}
	}
	else
		defaultInit();
	health = maxHealth();
}

//copy assignment operator
Enemy Enemy::operator=( const Enemy& other )
{
	seen = other.seen;
	name = other.name;
	strength = other.strength;
	agility = other.agility;
	stamina = other.stamina;
	xPos = other.xPos;
	yPos = other.yPos;
	return *this;
}

//Initializes default values for an Enemy.
//Designed to make them appear to fit into the game,
//But be obviously different from real enemies so the devs
//know there is a bug
void Enemy::defaultInit()
{
	name = "Exception";
	strength = 2;
	agility = 2;
	stamina = 2;
	health = maxHealth();
}

//enemy needs a unit (pointer) to attack and has an opportunity to miss
void Enemy::attack(Unit* other)
{
	RandInRange rander;
	int hitNum = rander(getHit());
	hitNum += hitNum / 2;
	int evaNum = rander(other->getEva());
	if(hitNum > evaNum)
	{
		int preMitigationDamage = strength + (rander(strength/2));
		int mitigation = rander(other->getSta()/2);
		int damage = preMitigationDamage - mitigation;
		if(damage < 0)
			damage = 0; 
		if(!other->takeDamage(damage))
		{
			gameRef->getCurrentDisplay()->addPlayerMessage("The " + name + " attacks you for " + Display::intToString(damage) + " damage.");
			playRandSound();
		}
		else
		{
			gameRef->getCurrentDisplay()->addPlayerMessage("This wont show cuz ur dead");
			PlaySound(TEXT("../sounds/dying.wav"), NULL, SND_FILENAME | SND_ASYNC);
			gameRef->getCurrentMap()->killEnemy(static_cast<Enemy*>(other));
		}
	}
	else
		gameRef->getCurrentDisplay()->addPlayerMessage("The " + name + " swings at you and misses.");
}

//Like in hero, grouped these since they are extremely similar
#pragma region Move Functions
void Enemy::moveW()
{
	this->xPos -= 1;
}

void Enemy::moveE()
{
	this->xPos += 1;
}

void Enemy::moveN()
{
	this->yPos -= 1;
}

void Enemy::moveS()
{
	this->yPos += 1;
}

void Enemy::moveNW()
{
	moveN();
	moveW();
}

void Enemy::moveNE()
{
	moveN();
	moveE();
}

void Enemy::moveSW()
{
	moveS();
	moveW();
}

void Enemy::moveSE()
{
	moveS();
	moveE();
}
#pragma endregion

//Prints the enemy name
void Enemy::print( ostream& o ) const
{
	o << name;
}

Enemy::~Enemy()
{

}

//Returns whether or not the enemy has been seen before
bool Enemy::getSeen() const
{
	return seen;
}

//Returns the first character of the Enemy name
inline Enemy::operator char() const
{
	return name[0];
}

//Updates the seen value for the enemy
void Enemy::Seen()
{
	seen = true;
}

//Evaluate information and do stuff
void Enemy::evaluateAI(GameManager* curGame)
{
	gameRef = curGame;
	Player* curPlay = curGame->getCurPlayer(); //Let's store our player
	Map* curMap = curGame->getCurrentMap();	//And store our map
	int xDist = curPlay->getX() - xPos; //This represents where we need to move towards in X
	int yDist = curPlay->getY() - yPos; //And here is where we need to move, via Y

	if(AbsoluteValue(xDist) > 1 || AbsoluteValue(yDist) > 1)
	{
		//If the player is not one square away, we need to evaluate where to move towards
		int xToEval; //This will be the actual x position of the square to evaluate
		int yToEval; //This will be the actual y position of the square to evaluate
		if(xDist > 0)
			xToEval = 1;
		else if (xDist < 0)
			xToEval = -1;
		else
			xToEval = 0;
		//So above we just looked either left a square, or right a square,
		//or same X pos depending on the dist
		//Below is the same for Y
		if(yDist > 0)
			yToEval = 1;
		else if (yDist < 0)
			yToEval = -1;
		else
			yToEval = 0;
		//So now we know what space we want to look at. So we check.
		if(curMap->checkPosition(yPos + yToEval,xPos + xToEval) == -1) //If the square is empty, just move there
			move(xToEval,yToEval);
		else //Otherwise, check the bordering squares
		{
			if(!xToEval) //If the player is on the same X, try to move left or right around obstacle
			{
				if(curMap->checkPosition(yPos + yToEval,xPos + xToEval -1) == -1) //check left spot
					move(xToEval - 1,yToEval);
				else if(curMap->checkPosition(yPos + yToEval,xPos + xToEval +1) == -1) //check right spot
					move(xToEval +1, yToEval);
				//if both fail, we do nothing
			}
			else if(!yToEval) //If on the same Y, try to move up or down around obstacle
			{
				if(curMap->checkPosition(yPos + yToEval - 1,xPos + xToEval) == -1) //check top spot
					move(xToEval,yToEval - 1);
				else if(curMap->checkPosition(yPos + yToEval + 1,xPos + xToEval) == -1) //check bottom spot
					move(xToEval, yToEval + 1);
				//again, there is no alternative so we give up 
			}
			else //Otherwise, try to move just along X or just along Y
			{
				if(xDist > yDist) //so if it's more X, we try X first
				{
					if(curMap->checkPosition(yPos,xPos + xToEval) == -1) //check pure X spot
						move(xToEval,0);
					else if(curMap->checkPosition(yPos + yToEval + 1,xPos) == -1) //check pure Y spot
						move(0, yToEval);
				}
				else
				{
					if(curMap->checkPosition(yPos + yToEval + 1,xPos) == -1) //check pure Y spot
						move(0, yToEval);
					else if(curMap->checkPosition(yPos,xPos + xToEval) == -1) //check pure X spot
						move(xToEval,0);
				}
			}
		}
	}
	else
	{
		//Otherwise, we know the Player is in range, so we attack him
		attack(curPlay);
	}

}

//Takes in a x and y (-1, 0, or 1) and picks the correct move
void Enemy::move( int x, int y )
{
	if(y == -1)
	{
		if(x == -1)
			moveNW();
		else if (x == 1)
			moveNE();
		else
			moveN();
	}
	else if(y == 1)
	{
		if(x == -1)
			moveSW();
		else if(x == 1)
			moveSE();
		else
			moveS();
	}
	else
	{
		if(x == -1)
			moveW();
		else
			moveE();
	}
}
